package com.programmingteam.wallpaperslideshow.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidParameterException;
import java.security.ProviderException;
import java.util.List;

import org.apache.http.client.ClientProtocolException;
import org.json.JSONException;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.location.Location;
import android.location.LocationManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Toast;

import com.programmingteam.util.HttpHelper;
import com.programmingteam.wallpaperslideshow.Config;
import com.programmingteam.wallpaperslideshow.R;
import com.programmingteam.wallpaperslideshow.contentprovider.ImageProvider;
import com.programmingteam.wallpaperslideshow.imagesearch.PhotoData;
import com.programmingteam.wallpaperslideshow.imagesearch.SearchController;
import com.programmingteam.wallpaperslideshow.ui.BaseActivity;
import com.programmingteam.wallpaperslideshow.ui.HomeActivity;

public class WallpaperService extends Service {
	public static final String TAG = "WallpaperSlideshow";

	private Looper mServiceLooper;
	private ServiceHandler mServiceHandler;
	private PowerManager.WakeLock mWakeLock;
	private NotificationManager mNotificationManager;
	private Notification mNotification;
	
	private static final int MESSAGE_NOREPEAT = 1;
	//private static final int MESSAGE_HANDLEALL = 2;

	// Handler that receives messages from the thread
	private final class ServiceHandler extends Handler {
		public ServiceHandler(Looper looper) {
			super(looper);
		}

		@Override
		public void handleMessage(Message msg) {
			Context context;
			SearchController searchController;
			PhotoData nextPhoto = null;
			File cacheDir;
			String fileName, errorMessage = "";
			HttpHelper httpHelper;
			ImageOperations imageOps;
			WallpaperManager wallpaperManager;
			CacheManager cacheManager;
			Location location = null;
			Intent broadcast;
			int wpHeight, wpWidth;
			long tid;
			
			setNotification(true);
			
			tid = Thread.currentThread().getId();
			context = getApplicationContext();
			try {

				// Step 0: read input message
				if (msg.obj != null && msg.obj instanceof Location) {
					location = (Location) msg.obj;
				}
				else if (msg.obj != null && msg.obj instanceof PhotoData) {
					nextPhoto = (PhotoData) msg.obj;
				}
				else {
					errorMessage = "Invalid service parameter (should be of type Location or PhotoData)";
					Log.w(TAG, "<SVC"+tid+"> " + errorMessage);
					throw new InvalidParameterException(errorMessage);
				}

				// Step 1: search
				if (location != null) {
					try {
						Log.d(TAG, "<SVC@"+tid+"> Search start (" + location.getLatitude() +", " + location.getLongitude()+")");
						searchController = new SearchController(location, context);
						nextPhoto = searchController.getNextPhoto();
						
						// Save to SD Card
						// cacheDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
						cacheDir = context.getFilesDir();
						fileName = nextPhoto.getProvider() + nextPhoto.getImageId() + ".jpg";
						
						// @TODO Control cacheDir!
						nextPhoto.setFile(new File(cacheDir, fileName));
						Log.d(TAG, "<SVC@"+tid+"> Search result: " + (nextPhoto != null ? nextPhoto.getImageUri() : "Error! No photo found!"));
					}
					catch (ProviderException e) {
						errorMessage = "Null search results, search failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (URISyntaxException e) {
						errorMessage = "Invalid search URI, search failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (JSONException e) {
						errorMessage = "Search response parsing error, search failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (ClientProtocolException e) {
						errorMessage = "HTTP protocol error, search failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (IOException e) {
						errorMessage = "IO Exception, search failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (NullPointerException e) {
						errorMessage = "Null pointer exception, search failed ["
								+ (e.getStackTrace()[0]).getFileName()
								+ (e.getStackTrace()[0]).getLineNumber() + "]";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (Exception e) {
						errorMessage = "General exception, search failed [" + e.getMessage() + "@"
								+ (e.getStackTrace()[0]).getFileName()
								+ (e.getStackTrace()[0]).getLineNumber() + "]";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
				}
				else {
					Log.d(TAG, "<SVC@"+tid+"> No location, search skipped.");
				}

				// Step 2: download
				if (nextPhoto != null && nextPhoto.getFile() != null && !nextPhoto.getFile().exists()) {
					try {
						Log.d(TAG, "<SVC@"+tid+"> Download file...");
						httpHelper = new HttpHelper();
						httpHelper.downloadFile(nextPhoto.getImageUri(), nextPhoto.getFile());
					}
					catch (MalformedURLException e) {
						errorMessage = "Invalid file URI, image download failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (IOException e) {
						errorMessage = "IO Exception, image download failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (Exception e) {
						errorMessage = "General exception, image download filed [" + e.getMessage() + "@"
								+ (e.getStackTrace()[0]).getFileName()
								+ (e.getStackTrace()[0]).getLineNumber() + "]";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
				}
				else {
					Log.d(TAG, "<SVC@"+tid+"> File is alredy cached, skipping download.");
				}

				// Step 3: resize, cache and set wallpaper
				if (nextPhoto != null && nextPhoto.getFile().exists()) {
					try {
						Log.d(TAG, "<SVC@"+tid+"> Resizing file...");
						imageOps = new ImageOperations();
						wallpaperManager = WallpaperManager.getInstance(context);
						wpHeight = wallpaperManager.getDesiredMinimumHeight();
						wpWidth = wallpaperManager.getDesiredMinimumHeight();
						imageOps.resizeImage(nextPhoto.getFile(), wpHeight, wpWidth);
						Log.d(TAG, "<SVC@"+tid+"> Resizing done.");

						Log.d(TAG, "<SVC@"+tid+"> Caching file...");
						cacheManager = new CacheManager(context);
						cacheManager.cacheImage(nextPhoto);
						cacheManager.clearCache(Config.Cache.NUM_THUMBNAILS, Config.Cache.NUM_IDS);
						Log.d(TAG, "<SVC@"+tid+"> Caching done");

						Log.d(TAG, "<SVC@"+tid+"> Wallpaper change...");
						if(isWallpaperChange()) {
							wallpaperManager.setStream(new FileInputStream(nextPhoto.getFile()));
						}
						Log.d(TAG, "<SVC@"+tid+"> Wallpaper change done");

						// Send broadcast to user interface
						broadcast = new Intent();
						broadcast.setAction(Config.Service.BROADCAST_ACTION_ON_DOWNLOADED);
						LocalBroadcastManager.getInstance(WallpaperService.this.getApplicationContext()).sendBroadcast(broadcast);
					}
					catch (FileNotFoundException e) {
						errorMessage = "Image file not found, wallpaper change failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (IOException e) {
						errorMessage = "IO Exception, wallpaper change failed";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
					catch (Exception e) {
						errorMessage = "General exception (resize, cache, set) filed [" + e.getMessage() + "@"
								+ (e.getStackTrace()[0]).getFileName()
								+ (e.getStackTrace()[0]).getLineNumber() + "]";
						Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
						e.printStackTrace();
						throw e;
					}
				}
				else {
					errorMessage = "Image file does not exist, wallpaper change failed";
					Log.w(TAG, "<SVC@"+tid+"> " + errorMessage);
					throw new IOException(errorMessage);
				}

			}
			catch (Exception e) {
				Log.d(TAG, "<SVC@"+tid+"> Error occured, killing service.");
				Toast.makeText(getApplicationContext(), "Wallpaper ERROR: " + errorMessage, Toast.LENGTH_SHORT).show();
			}
			finally {
				setNotification(false);
				this.removeMessages(MESSAGE_NOREPEAT);
				stopSelf(msg.arg1);
			}
		}
	}

	@Override
	public void onCreate() {
		// Start up the thread running the service. Note that we create a
		// separate thread because the service normally runs in the process's
		// main thread, which we don't want to block. We also make it
		// background priority so CPU-intensive work will not disrupt our UI.
		// Also there will be only one thread at a time, so messages will be queued
		HandlerThread thread;
		
		Log.d(TAG, "<SVC> Creating service");
		thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
		thread.start();

		// Get the HandlerThread's Looper and use it for our Handler
		mServiceLooper = thread.getLooper();
		mServiceHandler = new ServiceHandler(mServiceLooper);
		mNotificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
		mNotification = new Notification();
	}

	/**
	 * 1. F_PHOTODATA=true + D_PHOTODATA_ID=(long) imageId 
	 * 2. F_LOCATION=true => retreive location (static/track current is read from shared prefs) 
	 * 3. F_NEXTPHOTO=true (last search location is restored)
	 */
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		PowerManager powerManager;
		Message msg;
		boolean isPhotoData, isNextPhoto, isLocation;
		long photoId;

		Log.d(TAG, "<START_SVC> Starting service");
		powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
		mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
		mWakeLock.acquire();

		isPhotoData = intent.getBooleanExtra(Config.Const.F_PHOTODATA, false);
		isLocation = intent.getBooleanExtra(Config.Const.F_LOCATION, false);
		isNextPhoto = intent.getBooleanExtra(Config.Const.F_NEXTPHOTO, true);

		msg = mServiceHandler.obtainMessage();
		msg.arg1 = startId;

		if (isPhotoData) {
			photoId = intent.getLongExtra(Config.Const.D_PHOTODATA_ID, 0);
			msg.obj = getPhotoData(photoId);
			msg.what = MESSAGE_NOREPEAT;
		}
		else if (isLocation) {
			msg.obj = getLocation();
			msg.what = MESSAGE_NOREPEAT;
		}
		else if (isNextPhoto) {
			msg.obj = getLocation();
			msg.what = MESSAGE_NOREPEAT;
		}

		mServiceHandler.sendMessage(msg);
		// After killing - restart service and redeliver current intent
		return START_REDELIVER_INTENT;
	}

	@Override
	public IBinder onBind(Intent intent) {
		// We don't provide binding, so return null
		return null;
	}

	@Override
	public void onDestroy() {
		mServiceLooper.quit();
		mWakeLock.release();
	}

	private Location getLocation() {
		SharedPreferences prefs;
		Location location;
		int locationType;

		prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
		locationType = prefs.getInt(Config.SharedKeys.LOCATION_TYPE, Config.SharedDefaults.LOCATION_TYPE);

		switch (locationType) {
		case Config.Const.LOCATION_TRACK_CURRENT:
			location = getCurrentLocation();
			break;
		case Config.Const.LOCATOIN_STATIC:
			location = getSavedStaticLocation();
			break;
		default:
			location = null;
		}
		return location;
	}

//	private Location getSavedPreviousLocation() {
//		SharedPreferences prefs;
//		Location location;
//
//		prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
//		location = new Location("FAKE");
//		location.setLatitude(prefs.getFloat(Config.SharedKeys.PSEARCH_LATITUDE, Config.SharedDefaults.LOCATION_LATITUDE));
//		location.setLongitude(prefs.getFloat(Config.SharedKeys.PSEARCH_LONGITUDE, Config.SharedDefaults.LOCATION_LONGITUDE));
//		return location;
//	}

	private Location getSavedStaticLocation() {
		SharedPreferences prefs;
		Location location;

		prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
		location = new Location("FAKE");
		location.setLatitude(prefs.getFloat(Config.SharedKeys.LOCATION_LATITUDE, Config.SharedDefaults.LOCATION_LATITUDE));
		location.setLongitude(prefs.getFloat(Config.SharedKeys.LOCATION_LONGITUDE, Config.SharedDefaults.LOCATION_LONGITUDE));
		return location;
	}

	private Location getCurrentLocation() {
		LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		List<String> providerList = locationManager.getProviders(true);
		Location location = null;

		for (int i = providerList.size() - 1; i >= 0 && location == null; i--) {
			location = locationManager.getLastKnownLocation(providerList.get(i));
		}
		return location;
	}

	private PhotoData getPhotoData(long photoId) {
		PhotoData photoData = null;
		Cursor c = getContentResolver().query(ImageProvider.ImagesTable.CONTENT_URI, null, "_ID=" + photoId, null, null);
		if (c.moveToFirst()) {
			photoData = new PhotoData();
			photoData.setImageId(c.getLong(c.getColumnIndex(ImageProvider.ImagesTable.PHOTO_ID)));
			photoData.setProvider(c.getString(c.getColumnIndex(ImageProvider.ImagesTable.PHOTO_PROVIDER)));
			photoData.setFile(new File(c.getString(c.getColumnIndex(ImageProvider.ImagesTable.PHOTO_FILE))));
			photoData.setSourcePageUri(URI.create(c.getString(c.getColumnIndex(ImageProvider.ImagesTable.PHOTO_PAGE_URI))));
			photoData.setImageUri(URI.create(c.getString(c.getColumnIndex(ImageProvider.ImagesTable.PHOTO_URI))));
			photoData.setPhtotoDescription(c.getString(c.getColumnIndex(ImageProvider.ImagesTable.PHOTO_DESC)));
			photoData.setPhotoNearPlace(c.getString(c.getColumnIndex(ImageProvider.ImagesTable.PHOTO_PLACE)));
		}
		return photoData;
	}
	
	private boolean isWallpaperChange() {
		SharedPreferences prefs;
		boolean isWallpaperChange;
		prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
		isWallpaperChange = prefs.getBoolean(Config.SharedKeys.SET_WALLPAPER_ON_EVENT, Config.SharedDefaults.SET_WALLPAPER_ON_EVENT);
		return isWallpaperChange;
	}
	
	private void setNotification(boolean isServiceRunning) {
		boolean isNotificationEnabled;
		Intent notificationIntent;
		SharedPreferences prefs;
		PendingIntent mainActivityIntent;
		
//		intent = new Intent(getApplicationContext(), AlarmReceiver.class);
//		scheduledIntent = PendingIntent.getBroadcast(this, AlarmReceiver.REQUEST_CODE, intent, PendingIntent.FLAG_NO_CREATE);
//		if (scheduledIntent != null) {
//			isIntentSet = true;
//		}
		
		prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
		isNotificationEnabled = prefs.getBoolean(Config.SharedKeys.NOTIFICATION_ENABLED, Config.SharedDefaults.NOTIFICATION_ENABLED);
		
		if (isNotificationEnabled) {
//			//TODO send notification
			notificationIntent = new Intent(getApplicationContext(), HomeActivity.class);
			mainActivityIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
			if (isServiceRunning) {
				mNotification.when = System.currentTimeMillis();
				mNotification.icon = R.drawable.icon_notification;
				mNotification.setLatestEventInfo(getApplicationContext(), "Wallpaper Slideshow", "Service is working...", mainActivityIntent);
				mNotificationManager.notify(Config.Const.NOTIFY_SERVICE_ID, mNotification);
			}
//			else if (isIntentSet) {
//				mNotification.when = System.currentTimeMillis();
//				mNotification.icon = R.drawable.icon_notification_work;
//				mNotification.setLatestEventInfo(mContext.getApplicationContext(), "Wallpaper Slideshow", "Next wallpaper "+mTime.hour+":"+mTime.minute, mContentIntent);
//				mNotificationManager.notify(Config.Const.NOTIFY_SERVICE_ID, mNotification);
//			}
			else {
				mNotificationManager.cancel(Config.Const.NOTIFY_SERVICE_ID);
			}
		}
		
	}
};
